package me.ywy;

import java.io.FileInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * 
 * @author Simon.H
 * 
 * 计算Unity中AndroidObb的hash值
 * 
 * Unity Android打包时如果使用使用obb的话，会在APK包中持有obb文件的hash引用，obb文件如果被修改就无法加载，此类可以对手动修改的obb文件重新计算hash，计算出的结果替换到 apk中assets/bin/Data/settings.xml中即可正常加载手动修改过的obb文件
 * 
 */

public class UnityObbHash {
	public static String gen(String file) {
		FileInputStream fis = null;
		try {
			MessageDigest md = MessageDigest.getInstance("MD5");
			fis = new FileInputStream(file);
			byte[] buffer = new byte[63];
			int length = -1;
			long size = fis.available();
			fis.skip(size - Math.min(size, 65558L));
			while ((length = fis.read(buffer)) != -1) {
				md.update(buffer, 0, length);
			}
			BigInteger bi = new BigInteger(1, md.digest());
			return bi.toString(16);
		} catch (IOException ex) {
			ex.printStackTrace();
			return null;
		} catch (NoSuchAlgorithmException ex) {
			ex.printStackTrace();
			return null;
		} finally {
			if (null != fis) {
				try {
					fis.close();
				} catch (IOException ex) {
					ex.printStackTrace();
				}
			}
		}
	}

	public static void main(String[] args) {
		if (args.length <= 0) {
			return;
		}
		System.out.println(gen(args[0]));
		//System.out.println(gen("H:\\AndroidPacker\\CS\\Packtool\\packtool\\output\\1002\\800_Google800_0002678\\main.2.com.xmly.deadzombie.obb"));
		try {
			System.in.read();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

/* 
参考smali

.locals 10

const/4 v1, 0x0

const/4 v0, 0x0

:try_start_0
const-string v2, "MD5"

invoke-static {v2}, Ljava/security/MessageDigest;->getInstance(Ljava/lang/String;)Ljava/security/MessageDigest;

move-result-object v3

new-instance v4, Ljava/io/FileInputStream;

invoke-direct {v4, p0}, Ljava/io/FileInputStream;-><init>(Ljava/lang/String;)V

new-instance v2, Ljava/io/File;

invoke-direct {v2, p0}, Ljava/io/File;-><init>(Ljava/lang/String;)V

invoke-virtual {v2}, Ljava/io/File;->length()J

move-result-wide v6

const-wide/32 v8, 0x10016

invoke-static {v6, v7, v8, v9}, Ljava/lang/Math;->min(JJ)J

move-result-wide v8

sub-long/2addr v6, v8

invoke-virtual {v4, v6, v7}, Ljava/io/FileInputStream;->skip(J)J

const/16 v2, 0x400

new-array v5, v2, [B

move v2, v0

:goto_0
const/4 v6, -0x1

if-eq v2, v6, :cond_0

const/4 v6, 0x0

invoke-virtual {v3, v5, v6, v2}, Ljava/security/MessageDigest;->update([BII)V

invoke-virtual {v4, v5}, Ljava/io/FileInputStream;->read([B)I

move-result v2

goto :goto_0

:cond_0
invoke-virtual {v3}, Ljava/security/MessageDigest;->digest()[B
:try_end_0
.catch Ljava/io/FileNotFoundException; {:try_start_0 .. :try_end_0} :catch_0
.catch Ljava/io/IOException; {:try_start_0 .. :try_end_0} :catch_1
.catch Ljava/security/NoSuchAlgorithmException; {:try_start_0 .. :try_end_0} :catch_2

move-result-object v2

:goto_1
if-nez v2, :cond_1

move-object v0, v1

:goto_2
return-object v0

:catch_0
move-exception v2

move-object v2, v1

goto :goto_1

:catch_1
move-exception v2

move-object v2, v1

goto :goto_1

:catch_2
move-exception v2

move-object v2, v1

goto :goto_1

:cond_1
new-instance v1, Ljava/lang/StringBuffer;

invoke-direct {v1}, Ljava/lang/StringBuffer;-><init>()V

:goto_3
array-length v3, v2

if-ge v0, v3, :cond_2

aget-byte v3, v2, v0

and-int/lit16 v3, v3, 0xff

add-int/lit16 v3, v3, 0x100

const/16 v4, 0x10

invoke-static {v3, v4}, Ljava/lang/Integer;->toString(II)Ljava/lang/String;

move-result-object v3

const/4 v4, 0x1

invoke-virtual {v3, v4}, Ljava/lang/String;->substring(I)Ljava/lang/String;

move-result-object v3

invoke-virtual {v1, v3}, Ljava/lang/StringBuffer;->append(Ljava/lang/String;)Ljava/lang/StringBuffer;

add-int/lit8 v0, v0, 0x1

goto :goto_3

:cond_2
invoke-virtual {v1}, Ljava/lang/StringBuffer;->toString()Ljava/lang/String;

move-result-object v0

goto :goto_2 

*/
